/////////////////////////////////////////////////////////////////////////////
// 2023 -
//open source under Apache License Version 2.0
/////////////////////////////////////////////////////////////////////////////
//  Modifications by Kumo Inc.
// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#include <kmhdfs/client/raw_erasure_encoder.h>
#include <kmhdfs/client/ec_chunk.h>
#include <turbo/strings/str_format.h>
#include <turbo/log/logging.h>
#include <iostream>

namespace Hdfs {
    namespace Internal {
        RawErasureEncoder::RawErasureEncoder() : coderOptions(6, 3) {
        }

        RawErasureEncoder::RawErasureEncoder(ErasureCoderOptions &coderOptions) : coderOptions(coderOptions) {
            if (getNumAllUnits() >= RSUtil::getGF()->getFieldSize()) {
                THROW(InvalidParameter, "Invalid getNumDataUnits() and numParityUnits");
            }

            encodeMatrix = std::vector<int8_t>(getNumAllUnits() * getNumDataUnits());
            RSUtil::genCauchyMatrix(encodeMatrix, getNumAllUnits(), getNumDataUnits());

            gfTables = std::vector<int8_t>(getNumAllUnits() * getNumDataUnits() * 32);
            RSUtil::initTables(getNumDataUnits(), getNumParityUnits(), encodeMatrix,
                               getNumDataUnits() * getNumDataUnits(), gfTables);
        }

        int RawErasureEncoder::getNumDataUnits() const {
            return coderOptions.getNumDataUnits();
        }

        int RawErasureEncoder::getNumParityUnits() const {
            return coderOptions.getNumParityUnits();
        }

        int RawErasureEncoder::getNumAllUnits() const {
            return coderOptions.getNumAllUnits();
        }

        bool RawErasureEncoder::isAllowVerboseDump() const {
            return coderOptions.isAllowVerboseDump();
        }

        void RawErasureEncoder::encode(std::vector<std::shared_ptr<ByteBuffer> > &inputs,
                                       std::vector<std::shared_ptr<ByteBuffer> > &outputs) {
            std::shared_ptr<ByteBufferEncodingState> bbeState = std::shared_ptr<ByteBufferEncodingState>(
                new ByteBufferEncodingState(inputs, outputs));

            int dataLen = bbeState->encodeLength;
            if (dataLen == 0) {
                return;
            }

            std::vector<int> inputPositions(inputs.size());
            for (int i = 0; i < static_cast<int>(inputPositions.size()); i++) {
                if (inputs[i]) {
                    inputPositions[i] = static_cast<int>(inputs[i]->position());
                }
            }

            doEncode(bbeState);
            for (int i = 0; i < static_cast<int>(inputs.size()); i++) {
                if (inputs[i]) {
                    // dataLen bytes consumed
                    inputs[i]->position(inputPositions[i] + dataLen);
                }
            }
        }

        void RawErasureEncoder::encode(std::vector<std::shared_ptr<ECChunk> > &inputs,
                                       std::vector<std::shared_ptr<ECChunk> > &outputs) {
            std::vector<std::shared_ptr<ByteBuffer> > newInputs = CoderUtil::toBuffers(inputs);
            std::vector<std::shared_ptr<ByteBuffer> > newOutputs = CoderUtil::toBuffers(outputs);
            encode(newInputs, newOutputs);
        }

        void RawErasureEncoder::doEncode(const std::shared_ptr<ByteBufferEncodingState> &encodingState) {
            CoderUtil::resetOutputBuffers(encodingState->outputs, encodingState->encodeLength);
            RSUtil::encodeData(gfTables, encodingState->inputs, encodingState->outputs);
        }

        void RawErasureEncoder::release() {
        }
    }
}
